// -*- c++ -*-
#ifndef __CMATHFIXED16_H__
#define __CMATHFIXED16_H__


#include "StdDefs.h"
//#include "CClass.h"
#include "SharedSettings.h"



typedef CMathFixed16_Type	fixed16;


#define CMathFixed16_One								( 1 << CMathFixed16_NumberOfFractionalBits )
#define CMathFixed16_PI									CMathFixed16_FloatToFixed16( _PI )
#define CMathFixed16_PI2								CMathFixed16_FloatToFixed16( _PI2 )

#define CMathFixed16_FloatToFixed16(v)					( (fixed16)( CMathFixed16_One * (v) ) )
#define CMathFixed16_Fixed16ToInt16(v)					( (int16)( (v) >> CMathFixed16_NumberOfFractionalBits ) )
#define CMathFixed16_Int16ToFixed16(v)					( (int16)( (v) << CMathFixed16_NumberOfFractionalBits ) )

#define CMathFixed16_RadiansToDegrees(v)				CMathFixed16::Mul( (v), CMathFixed16_FloatToFixed( 180.0f / _PI ) )

#if defined( GLfixed_fxp )
	#if( CMathFixed16_NumberOfFractionalBits == GLfixed_fxp )
		#define CMathFixed16_FixedToGLfixed(v)			(v)
		#define CMathFixed16_GLfixedToFixed(v)			(v)
	#else
		#define CMathFixed16_FixedToGLfixed(v)			(GLfixed_fxp >= CMathFixed16_NumberOfFractionalBits)?			\
															(v)<<(GLfixed_fxp - CMathFixed16_NumberOfFractionalBits) :\
															(v)>>(CMathFixed16_NumberOfFractionalBits - GLfixed_fxp)

		#define CMathFixed16_GLfixedToFixed(v)			(GLfixed_fxp >= CMathFixed16_NumberOfFractionalBits)?			\
															(v)>>(GLfixed_fxp - CMathFixed16_NumberOfFractionalBits) :\
															(v)<<(CMathFixed16_NumberOfFractionalBits - GLfixed_fxp)
	#endif
#endif




class CMathFixed16 
{
public:
	static const uint32		FBits;
	static const fixed16	One;

	static const fixed16	PI;
	static const fixed16	PI2;

	static const fixed16	Degrees_15;
	static const fixed16	Degrees_33;
	static const fixed16	Degrees_45;
	static const fixed16	Degrees_90;
	static const fixed16	Degrees_180;
	static const fixed16	Degrees_270;
	static const fixed16	Degrees_360;

	static const fixed16	UnitTolerance;
	static const fixed16	ClampToUnitError;

	//
	static fixed16			Abs( fixed16 val );
	static fixed16			Max( fixed16 val1, fixed16 val2 );
	static fixed16			Min( fixed16 val1, fixed16 val2 );

	//
	static fixed16			Ceil( fixed16 val );
	static fixed16			Floor( fixed16 val );
	static fixed16			Round( fixed16 val );

	// 
	static fixed16			Div( fixed16 num, fixed16 denom );
	static fixed16			Mul( fixed16 val1, fixed16 val2 );

	//
	static fixed16			Inv( fixed16 val );

	static fixed16          Lerp( fixed16 begin, fixed16 end, fixed16 val );
    static fixed16          EaseInOut( fixed16 interpolation );  // input is from 0 to 1, output is from 0 to 1; pass output to Lerp to interpolate between 2 points
    static fixed16          EaseIn( fixed16 interpolation );
    static fixed16          EaseOut( fixed16 interpolation );

	/*
	*  Integer Square Root function
	*  Contributors include Arne Steinarson for the basic approximation idea, Dann 
	*  Corbit and Mathew Hendry for the first cut at the algorithm, Lawrence Kirby 
	*  for the rearrangement, improvements and range optimization and Paul Hsieh 
	*  for the round-then-adjust idea.
	*
	*  Source: http://www.azillionmonkeys.com/qed/sqroot.html
	*  (originally posted on comp.graphics.algorithms)
	*/
	static fixed16			Sqrt( fixed16 val );

	//
	static fixed16			GetFraction( fixed16 val );
	static fixed16			Clamp( fixed16 val, fixed16 min = -CMathFixed16_One, fixed16 max = CMathFixed16_One );
	static fixed16			ClampToUnit( fixed16 val );

	/* 
	* Solves the quadratic equation x = ( -b +/- ( b*b ) - ( 4*a*c) ) / ( 2*a ).
	* Returns true if the roots are real.
	*/
	static boolean			SolveQuadraticForReals( fixed16* root1, fixed16* root2, 
													fixed16 a, fixed16 b, fixed16 c );
	
	// trig - conv
	static fixed16			DegreesToRadians( fixed16 degrees );
	static fixed16			RadiansToDegrees( fixed16 radians );

	// trig - funcs
	static fixed16			ACos( fixed16 b );
	static fixed16			ASin( fixed16 a );
	static fixed16			ATan( fixed16 h );
    static fixed16          ATan2( fixed16 y, fixed16 x ); // if x=y, x & y must be less than (int) 32
    static fixed16          ATan2i( int32 y, int32 x ); // much higher range of x & y as inputs than the fixed version
	static fixed16			Cos( fixed16 degrees );
	static fixed16			Sin( fixed16 degrees );
	static fixed16			Tan( fixed16 degrees );
};





inline
fixed16 CMathFixed16::Abs( fixed16 val )
{
	return val > 0 ? val : -val;
}


inline
fixed16 CMathFixed16::Max( fixed16 val1, fixed16 val2 )
{
	return ( val1 > val2 )? val1 : val2;
}


inline
fixed16 CMathFixed16::Min( fixed16 val1, fixed16 val2 )
{
	return ( val1 < val2 )? val1 : val2;
}


inline
fixed16 CMathFixed16::Ceil( fixed16 val )
{
	fixed16 ret;

    ret = Floor( val );

	if( val & ( (~0) ^ ( (~0) << FBits ) ) ) 
		ret	+= One;

	return ret;
}


inline
fixed16 CMathFixed16::Floor( fixed16 val )
{
	return val & ( (~0) << FBits );
}


inline
fixed16 CMathFixed16::Round( fixed16 val )
{
	fixed16 ret;

    ret = Floor( val );

	if( ( val & ( (~0) ^ ( (~0) << FBits ) ) ) >= ( One >> 1 ) )
		ret += One;

	return ret;
}


inline
fixed16 CMathFixed16::Div( fixed16 num, fixed16 denom )
{
	CMathFixed_Type64 v64Num = num;
	CMathFixed_Type64 v64Denom = denom;

	return (fixed16)( ( v64Num << FBits ) / v64Denom );
}

inline
fixed16 CMathFixed16::Mul( fixed16 val1, fixed16 val2 )
{
	CMathFixed_Type64 v64A = val1;
	CMathFixed_Type64 v64B = val2;

	return (fixed16)( ( v64A * v64B ) >> FBits );
}


inline
fixed16 CMathFixed16::GetFraction( fixed16 val )
{
	return val - ( ( val >> FBits ) << FBits );
}


inline
fixed16 CMathFixed16::Clamp( fixed16 val, fixed16 min, fixed16 max )
{
	if( val < min )
		return min;
	else
	if( val > max )
		return max;
	
	return val;
}


inline
fixed16 CMathFixed16::ClampToUnit( fixed16 val )
{
	ASSERT( Abs( val ) < ClampToUnitError );
	
	if( val < -One )
		val = -One;
	else
	if( val > One )
		val = One;

	return val;
}


inline
fixed16 CMathFixed16::DegreesToRadians( fixed16 degrees )
{
	return Mul( degrees, CMathFixed16_PI ) / 180;
}


inline
fixed16 CMathFixed16::RadiansToDegrees( fixed16 radians )
{
	return Div( radians * 180, CMathFixed16_PI );
}


inline
fixed16 CMathFixed16::Cos( fixed16 degrees )
{
	return Sin( degrees + CMathFixed16::Degrees_90 );
}


inline
fixed16 CMathFixed16::Tan( fixed16 degrees )
{
	fixed16 cos = Cos( degrees );
	
	return ( cos )? Div( Sin( degrees ), cos ) : MAX_INT32;
}

inline
fixed16 CMathFixed16::Inv( fixed16 val )
{
	return Div( One, val );
}


inline
fixed16 CMathFixed16::Lerp( fixed16 begin, fixed16 end, fixed16 val )
{
	return begin + CMathFixed16::Mul( ( end - begin ), val );
}


inline
fixed16 CMathFixed16::EaseInOut( fixed16 interpolation )
{
    return (CMathFixed16::One - CMathFixed16::Cos(interpolation*180))/2;
}


inline
fixed16 CMathFixed16::EaseIn( fixed16 interpolation )
{
    return CMathFixed16::One - CMathFixed16::Cos(interpolation*90);
}


inline
fixed16 CMathFixed16::EaseOut( fixed16 interpolation )
{
    return - CMathFixed16::Cos(CMathFixed16::Degrees_90 + interpolation*90);
}


#endif // #define __CMATHFIXED16_H__
